﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace B2BCore
{
    /// <summary>
    /// Listens for incoming connections
    /// </summary>
    public abstract class ConnectionListener
    {
        private TcpListener _tcpListener;
        private bool _listening;

        /// <summary>
        /// Function to handle a new connection
        /// </summary>
        /// <param name="socket">The socket on which the connection is formed</param>
        public abstract void OnNewConnection(Socket socket);

        private Thread _thread;

        /// <summary>
        /// Starts the tcp listner
        /// </summary>
        /// <param name="port">The port number to start the listener on</param>
        /// <returns></returns>
        public bool Start(int port)
        {
            if (_thread == null)
            {
                if (_tcpListener == null)
                    _tcpListener = new TcpListener(System.Net.IPAddress.Any, port);

                _listening = true;

                _thread = new Thread(new ThreadStart(ThreadProc));
                _thread.Name = "TcpListener Thread";
                _thread.Start();

                return true;
            }

            return false;
        }

        private void ThreadProc()
        {
            _tcpListener.Start();

            while(_listening)
            {
                if (_tcpListener.Pending())
                {
                    Socket socket = _tcpListener.AcceptSocket();
                    if (socket.Connected)
                        OnNewConnection(socket);
                }

                Thread.Sleep(100);
            }
        }

        /// <summary>
        /// Stop the connection
        /// </summary>
        public void Stop()
        {
            if (_tcpListener == null)
                return;

            _listening = false;

            
            _tcpListener.Stop();
            _thread = null;

        }

    }

    /// <summary>
    /// Connection
    /// </summary>
    public class Connection
    {
        private TcpClient _tcpClient;
        private NetworkStream _stream;

        /// <summary>
        /// Opens a connection to a specified address and port
        /// </summary>
        /// <param name="ipAddress">IP Address which to open a connection to.</param>
        /// <param name="port">The port to open the connection on.</param>
        /// <returns>True if connected, false otherwise.</returns>
        /// <exception cref="SocketException"></exception>
        /// <exception cref="ArgumentNullException"></exception>
        /// 
        public bool Connect(IPAddress ipAddress, int port)
        {
            if (ipAddress == null) 
                throw new ArgumentNullException("ipAddress");

            Close();

            if (_tcpClient == null)
                _tcpClient = new TcpClient();

            _tcpClient.Connect(ipAddress, port);

            _stream = _tcpClient.GetStream();

            return true;
        }

        /// <summary>
        /// Closes the connection
        /// </summary>
        public void Close()
        {
            if (_tcpClient == null || !IsConnected)
                return;

            _tcpClient.Close();
            _tcpClient = null;
        }

        /// <summary>
        /// Sends some data over the network connection
        /// </summary>
        /// <param name="data">The byte array to send</param>
        /// <returns></returns>
        public bool Send(byte[] data)
        {
            if (data == null) 
                throw new ArgumentNullException("data");

            if (!IsConnected || _stream == null)
                return false;

            _stream.Write(data, 0, data.Length);

            return true;
        }

        /// <summary>
        /// Check to see if there is an active connection
        /// </summary>
        public bool IsConnected
        {
            get { return _tcpClient.Connected; }
        }
    }
}